package common;

import java.util.*;

/**
 * A simple cache class that can cache objects and then
 * return an old version of the objects you create. This
 * class is created to avoid multiple creations of  
 * immutable objects.
 * 
 * @author Fredrik Bystam
 * @version 2011.10.17
 *
 * @param <T> 
 */
public class SimpleCache<T> {
	
	private static final int DEFAULT_CAPACITY = 10;
	private final Map<T,T> map;
	private final Queue<T> queue;
	private final int capacity;
	
	/**
	 * Create a new SimpleCache with the specified maximum capacity.
	 * If input capacity is equal to zero or less, then a default 
	 * capacity of 10 is chosen.
	 * @param capacity The capacity of the cache
	 */
	public SimpleCache(int capacity) {
		map = new HashMap<T,T>();
		queue = new LinkedList<T>();
		this.capacity = capacity > 0 ? capacity : DEFAULT_CAPACITY;
	}
	
	/**
	 * Put an element in the cache. If the new size exceeds the capacity,
	 * then the oldest element in the cache will be removed.
	 * @param element The new element to be added
	 */
	public synchronized void put (T element){ 
		T old  = map.put(element, element);
		if (old == null) // Only add element to queue if the map did not contain it
			queue.add(element);
		if (map.size() > capacity){
			map.remove(queue.poll());
		}
	}
	
	/**
	 * Retrieve an old version of an element from the cache,
	 * or null if it was not present.
	 * @param element The key element to search with in the cache
	 * @return An older version of the element, or null if it wasn't present
	 */
	public synchronized T get(T element){
		return map.get(element);
	}
	
	/**
	 * Search the cache for the input element. If a matching element
	 * was found, then the older representation of this object will be returned.
	 * If no match was found, it will add the input argument to the cache and return
	 * the new input. This method can be described as a combination of get and put.
	 * @param element The element to match with cached elements
	 * @return An old version of the object if found, else the input argument
	 */
	public synchronized T match(T element) {
		T old = get(element);
		if (old == null){
			put(element);
			return element;
		} else {
			return old;
		}
	}
}
